/*
 * rd_stats.c: Read system statistics
 * (C) 1999-2019 by Sebastien GODARD (sysstat <at> orange.fr)
 *
 ***************************************************************************
 * This program is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU General Public License as published  by  the *
 * Free Software Foundation; either version 2 of the License, or (at  your *
 * option) any later version.                                              *
 *                                                                         *
 * This program is distributed in the hope that it  will  be  useful,  but *
 * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
 * for more details.                                                       *
 *                                                                         *
 * You should have received a copy of the GNU General Public License along *
 * with this program; if not, write to the Free Software Foundation, Inc., *
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
 ***************************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <fcntl.h>

#include "sv_platform.h"
#include "rd_stats.h"

#define MINIMUM(a,b)	((a) < (b) ? (a) : (b))

int  net_fd = -1;
static char net_buf[24576]; //24KB
int  uptime_fd = -1;

/*
 ***************************************************************************
 * Read machine uptime, independently of the number of processors.
 *
 * OUT:
 * @uptime	Uptime value in hundredths of a second.
 ***************************************************************************
 */
U64 read_uptime()
{
	char line[128] = {0};
	unsigned long up_sec, up_cent;
	U64 uptime = 0;
	struct timeval tv;
	
	if (uptime_fd <= 0)
	{
		uptime_fd = open(UPTIME, O_RDONLY);

		if (uptime_fd <= 0)
		{
			MNG_FATAL("open %s failed.", UPTIME);
			return 0;
		}
	}
	
	lseek(uptime_fd, 0, SEEK_SET);
	
	if (read(uptime_fd, line, sizeof(line) - 1) < 0)
	{
		MNG_FATAL("read %s failed. %d:%s", UPTIME, errno, strerror(errno));
		close(uptime_fd);
		uptime_fd = -1;
		return 0;
	}
	else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2)
	{
		uptime = ((unsigned long long) up_sec * 100 + (unsigned long long) up_cent) * 10000;
		gettimeofday(&tv, NULL);
		uptime += tv.tv_usec % 10000;
	}
	else
	{
		MNG_FATAL("uptime format: %s error", line);
		close(uptime_fd);
		uptime_fd = -1;
		return 0;
	}

	return uptime;
}

/*
 ***************************************************************************
 * Read network interfaces statistics from /proc/net/dev.
 *
 * IN:
 * @st_net_dev	Structure where stats will be saved.
 * @nr_alloc	Total number of structures allocated. Value is >= 1.
 *
 * OUT:
 * @st_net_dev	Structure with statistics.
 *
 * RETURNS:
 * Number of interfaces read, or -1 if the buffer was too small and
 * needs to be reallocated.
 ***************************************************************************
 */
__nr_t read_net_dev(struct stats_net_dev *st_net_dev, __nr_t nr_alloc, U64 *p_stats_net_dev_curr_time)
{
	struct stats_net_dev *st_net_dev_i;
	char iface[MAX_IFACE_LEN];
	__nr_t dev_read = 0;
	int pos;
	char *line = NULL;
	char *p_end_line;

	if (net_fd <= 0)
	{
		net_fd = open(NET_DEV, O_RDONLY);
		
		if (net_fd <= 0)
		{
			MNG_FATAL("open %s failed.", NET_DEV);
			return EOPEN;
		}
	}
	
	lseek(net_fd, 0, SEEK_SET);
	
	if (read(net_fd, net_buf, sizeof(net_buf) - 1) < 0)
	{
		MNG_FATAL("read %s failed. %d:%s", NET_DEV, errno, strerror(errno));
		close(net_fd);
		net_fd = -1;
		return EREAD;
	}

	*p_stats_net_dev_curr_time = read_uptime(); //us

	net_buf[sizeof(net_buf) - 1] = '\0';
	
	line = net_buf;
	while((p_end_line = strchr(line, '\n')) != NULL) {
		*p_end_line = '\0';
		pos = strcspn(line, ":");
		if (pos < strlen(line)) {

			if (dev_read + 1 > nr_alloc) {
				dev_read = ECOUNTOVER;
				break;
			}

			st_net_dev_i = st_net_dev + dev_read++;
			strncpy(iface, line, MINIMUM(pos, MAX_IFACE_LEN - 1));
			iface[MINIMUM(pos, MAX_IFACE_LEN - 1)] = '\0';
			sscanf(iface, "%s", st_net_dev_i->interface); /* Skip heading spaces */
			sscanf(line + pos + 1, "%llu %llu %*u %*u %*u %*u %llu %llu %llu %llu "
			       "%*u %*u %*u %*u %*u %llu",
			       &st_net_dev_i->rx_bytes,
			       &st_net_dev_i->rx_packets,
			       &st_net_dev_i->rx_compressed,
			       &st_net_dev_i->multicast,
			       &st_net_dev_i->tx_bytes,
			       &st_net_dev_i->tx_packets,
			       &st_net_dev_i->tx_compressed);
		}

		line = p_end_line + 1;
	}

	return dev_read;
}


